package com.zxj.geekstudy.spring.db;

import com.mysql.jdbc.StringUtils;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zxj.geekstudy.spring.db.po.Student;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * 3）配置 Hikari 连接池，改进上述操作。提交代码到 GitHub。
 */
public class HikariDB {

    private static ThreadLocal<Connection> threadLocalConn = new ThreadLocal<>();

    private static HikariDataSource ds;

    static {
        ds = getDataSource();
    }

    public static HikariDataSource getDataSource() {
        //连接池配置
        HikariConfig config = new HikariConfig();
        config.setDriverClassName("com.mysql.jdbc.Driver");
        config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/geek_study?user=root&password=root&useUnicode=true&characterEncoding=utf8");
        // 如果缓存设置是禁用缓存，上述参数都没有任何影响。您必须将此参数设置为true
        config.addDataSourceProperty("cachePrepStmts", true);
        // 这设置MySQL驱动程序每个连接缓存的准备语句数量。默认值为保守值25。我们建议将其设置在250-500之间。
        config.addDataSourceProperty("prepStmtCacheSize", 500);
        // 这是驱动程序将缓存的已准备好的SQL语句的最大长度。MySQL的默认值为256。
        // 根据我们的经验，特别是对于像Hibernate这样的ORM框架，这个默认值远远低于生成的语句长度的阈值。我们推荐的设置是2048。
        config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
        // MySQL支持服务器端准备语句，这可以大大提高性能。将此属性设置为true。
        config.addDataSourceProperty("useServerPrepStmts", true);
        // 此属性控制从池返回的连接的默认自动提交行为。它是一个布尔值。 默认值：true
        config.setAutoCommit(true);
        // 此属性控制客户端（即您）将等待来自池的连接的最大毫秒数。如果在没有可用连接的情况下超过此时间，则会抛出SQLException。
        // 最低可接受的连接超时时间为250 ms。 默认值：30000（30秒）
        config.setConnectionTimeout(5000);
        // 此属性控制允许连接在池中闲置的最长时间。 此设置仅适用于minimumIdle定义为小于maximumPoolSize。一旦池达到连接，空闲连接将不会退出minimumIdle。
        // 连接是否因闲置而退出，最大变化量为+30秒，平均变化量为+15秒。在超时之前，连接永远不会退出。值为0意味着空闲连接永远不会从池中删除。
        // 允许的最小值是10000ms（10秒）。 默认值：600000（10分钟）
        config.setIdleTimeout(600000);
        // 此属性控制池中连接的最大生存期。正在使用的连接永远不会退休，只有在关闭后才会被删除。在逐个连接的基础上，应用较小的负面衰减来避免池中的大量消失。
        // 我们强烈建议设置此值，并且应该比任何数据库或基础设施规定的连接时间限制短几秒。 值为0表示没有最大寿命（无限寿命），当然是idleTimeout设定的主题。
        // 默认值：1800000（30分钟）
        config.setMaxLifetime(1800000);
        // 如果您的驱动程序支持JDBC4，我们强烈建议您不要设置此属性。这是针对不支持JDBC4的“传统”驱动程序Connection.isValid() API。
        // 这是在连接从池中获得连接以确认与数据库的连接仍然存在之前将要执行的查询。再一次，尝试运行没有此属性的池，如果您的驱动程序不符合JDBC4的要求，
        // HikariCP将记录一个错误以告知您。 默认值：无
        config.setConnectionTestQuery("SELECT 1");
        // 该属性控制HikariCP尝试在池中维护的最小空闲连接数。如果空闲连接低于此值并且连接池中的总连接数少于此值maximumPoolSize，
        // 则HikariCP将尽最大努力快速高效地添加其他连接。但是，为了获得最佳性能和响应尖峰需求，我们建议不要设置此值，
        // 而是允许HikariCP充当固定大小的连接池。 默认值：与maximumPoolSize相同
        config.setMinimumIdle(50);
        // 此属性控制池允许达到的最大大小，包括空闲和正在使用的连接。基本上这个值将决定到数据库后端的最大实际连接数。
        // 对此的合理价值最好由您的执行环境决定。当池达到此大小并且没有空闲连接可用时，对getConnection（）的调用将connectionTimeout在超时前阻塞达几毫秒。
        // 请阅读关于游泳池尺寸。 默认值：10
        config.setMaximumPoolSize(100);
        // 该属性仅通过编程配置或IoC容器可用。该属性允许您指定池使用的Codahale / Dropwizard 实例MetricRegistry来记录各种指标。
        // 有关 详细信息，请参阅Metrics维基页面。 默认值：无
//        config.setMetricRegistry();
        // 该属性仅通过编程配置或IoC容器可用。该属性允许您指定池使用的Codahale / Dropwizard 的实例HealthCheckRegistry来报告当前的健康信息。
        // 有关 详细信息，请参阅健康检查 wiki页面。 默认值：无
//        config.setHealthCheckRegistry();
        // 此属性表示连接池的用户定义名称，主要出现在日志记录和JMX管理控制台中以识别池和池配置。 默认：自动生成
        config.setPoolName("HikariDB-Test");
        return new HikariDataSource(config);
    }

    public static Connection getConnection() {
        return threadLocalConn.get();
    }

    public int insert(String name, BigDecimal score) throws Exception {
        PreparedStatement ps = null;
        try {
            String sql = "INSERT INTO `student` (`name`, `score`) VALUES ( ?, ? )";
            ps = getConnection().prepareStatement(sql);
            ps.setString(1, name);
            ps.setBigDecimal(2, score);
            return ps.executeUpdate();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

    public int update(String name, BigDecimal score) throws Exception {
        if (StringUtils.isEmptyOrWhitespaceOnly(name) || score == null) {
            return 0;
        }
        PreparedStatement ps = null;
        try {
            String sql = "UPDATE `student` SET `score`= ? WHERE (`name` = ?)";
            ps = getConnection().prepareStatement(sql);
            ps.setBigDecimal(1, score);
            ps.setString(2, name);
            return ps.executeUpdate();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

    public int delete(String name) throws Exception {
        if (StringUtils.isEmptyOrWhitespaceOnly(name)) {
            return 0;
        }
        PreparedStatement ps = null;
        try {
            String sql = "DELETE FROM `student` WHERE (`name` = ?)";
            ps = getConnection().prepareStatement(sql);
            ps.setString(1, name);
            return ps.executeUpdate();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    }

    public List<Student> query(String name) throws Exception {
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        try {
            String sql = "SELECT * FROM `student`";
            if (!StringUtils.isEmptyOrWhitespaceOnly(name)) {
                sql += " WHERE name = ?";
            }
            ps = getConnection().prepareStatement(sql);
            if (!StringUtils.isEmptyOrWhitespaceOnly(name)) {
                ps.setString(1, name);
            }
            resultSet = ps.executeQuery();
            List<Student> list = new ArrayList<>();
            // 5.处理结果集（针对查询）
            while (resultSet.next()) {
                // 一次循环处理一行，一行转换成一个对象
                Student student = new Student(resultSet.getInt(1), resultSet.getString(2), resultSet.getBigDecimal(3));
                list.add(student);// 添加到集合
            }
            return list;
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }
            if (ps != null) {
                ps.close();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Connection connection = ds.getConnection();
        threadLocalConn.set(connection);
        try {
            connection.setAutoCommit(false);
            HikariDB hikariDB = new HikariDB();
            hikariDB.insert("stu1", new BigDecimal(10));
            hikariDB.insert("stu2", new BigDecimal(20));
            hikariDB.insert("stu3", new BigDecimal(30));
            System.out.println(hikariDB.query("stu1"));
            System.out.println(hikariDB.query(null));
            hikariDB.update("stu1", new BigDecimal(100));
            hikariDB.delete("stu2");
            System.out.println(hikariDB.query(null));
            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connection.rollback();
            // 关闭资源
            connection.close();
        }

    }
}
